#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>

#include "testopt.h"

#define TAG "test_aenc"

#define AEC_SAMPLE_RATE 8000
#define AEC_SAMPLE_TIME 10

#define IMP_AUDIO_BUF_SIZE (5 * (AEC_SAMPLE_RATE * sizeof(short) * AEC_SAMPLE_TIME / 1000))
#define IMP_AUDIO_RECORD_NUM 200

#define IMP_AUDIO_RECORD_ALGO_AI_FILE "/tmp/record_algo_ai_file.pcm"
#define IMP_AUDIO_RECORD_VOLTEST_FILE "/tmp/record_voltest_file.pcm"
#define IMP_AUDIO_RECORD_FILE "/tmp/record_file.pcm"
#define IMP_AUDIO_ENCODE_FILE "/tmp/record_file.g711"
#define IMP_AUDIO_PLAY_FILE "/tmp/play_file.pcm"
#define IMP_AUDIO_FILTER_FILE "/tmp/filter_file.pcm"

#define TMP_RECORD_FILE "tmp_record.pcm"  /*tmp record file for encode*/

extern TEST_ARGS testArgs;
extern char fileNameBuf[NAME_BUF_SIZE];

static char inFileName[256] = "ai";
static char outFileName[256] = "";

static int type = -1;

static void *audio_record_thread(void *argv)
{
	int ret = -1;
	int record_num = 0;

	FILE *record_file = fopen(TMP_RECORD_FILE, "wb");
	if(record_file == NULL) {
		IMP_LOG_ERR(TAG, "fopen %s failed\n", TMP_RECORD_FILE);
		return NULL;
	}

	/* Step 1: set public attribute of AI device. */
	int devID = 1;
	IMPAudioIOAttr attr;
	attr.samplerate = AUDIO_SAMPLE_RATE_8000;
	attr.bitwidth = AUDIO_BIT_WIDTH_16;
	attr.soundmode = AUDIO_SOUND_MODE_MONO;
	attr.frmNum = 20;
	attr.numPerFrm = 400;
	attr.chnCnt = 1;
	ret = IMP_AI_SetPubAttr(devID, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "set ai %d attr err: %d\n", devID, ret);
		return NULL;
	}

	memset(&attr, 0x0, sizeof(attr));
	ret = IMP_AI_GetPubAttr(devID, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "get ai %d attr err: %d\n", devID, ret);
		return NULL;
	}

	IMP_LOG_INFO(TAG, "Audio In GetPubAttr samplerate : %d\n", attr.samplerate);
	IMP_LOG_INFO(TAG, "Audio In GetPubAttr   bitwidth : %d\n", attr.bitwidth);
	IMP_LOG_INFO(TAG, "Audio In GetPubAttr  soundmode : %d\n", attr.soundmode);
	IMP_LOG_INFO(TAG, "Audio In GetPubAttr     frmNum : %d\n", attr.frmNum);
	IMP_LOG_INFO(TAG, "Audio In GetPubAttr  numPerFrm : %d\n", attr.numPerFrm);
	IMP_LOG_INFO(TAG, "Audio In GetPubAttr     chnCnt : %d\n", attr.chnCnt);

	/* Step 2: enable AI device. */
	ret = IMP_AI_Enable(devID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "enable ai %d err\n", devID);
		return NULL;
	}

	/* Step 3: set audio channel attribute of AI device. */
	int chnID = 0;
	IMPAudioIChnParam chnParam;
	chnParam.usrFrmDepth = 20;
	ret = IMP_AI_SetChnParam(devID, chnID, &chnParam);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "set ai %d channel %d attr err: %d\n", devID, chnID, ret);
		return NULL;
	}

	memset(&chnParam, 0x0, sizeof(chnParam));
	ret = IMP_AI_GetChnParam(devID, chnID, &chnParam);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "get ai %d channel %d attr err: %d\n", devID, chnID, ret);
		return NULL;
	}

	IMP_LOG_INFO(TAG, "Audio In GetChnParam usrFrmDepth : %d\n", chnParam.usrFrmDepth);

	/* Step 4: enable AI channel. */
	ret = IMP_AI_EnableChn(devID, chnID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record enable channel failed\n");
		return NULL;
	}

	/* Step 5: Set audio channel volume. */
	int chnVol = 60;
	ret = IMP_AI_SetVol(devID, chnID, chnVol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record set volume failed\n");
		return NULL;
	}

	ret = IMP_AI_GetVol(devID, chnID, &chnVol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record get volume failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio In GetVol    vol : %d\n", chnVol);

	int aigain = 20;
	ret = IMP_AI_SetGain(devID, chnID, aigain);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Set Gain failed\n");
		return NULL;
	}

	ret = IMP_AI_GetGain(devID, chnID, &aigain);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Get Gain failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio In GetGain    gain : %d\n", aigain);

	/* Enable Aec */
	ret = IMP_AI_EnableAec(devID, chnID, 0, 0);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio enable aec failed\n");
		return NULL;
	}

	while(1) {
		/* Step 6: get audio record frame. */

		ret = IMP_AI_PollingFrame(devID, chnID, 1000);
		if (ret != 0 ) {
			IMP_LOG_ERR(TAG, "Audio Polling Frame Data error\n");
		}
		IMPAudioFrame frm;
		ret = IMP_AI_GetFrame(devID, chnID, &frm, BLOCK);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "Audio Get Frame Data error\n");
			return NULL;
		}

		/* Step 7: Save the recording data to a file. */
		fwrite(frm.virAddr, 1, frm.len, record_file);

		/* Step 8: release the audio record frame. */
		ret = IMP_AI_ReleaseFrame(devID, chnID, &frm);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "Audio release frame data error\n");
			return NULL;
		}

		if(++record_num >= IMP_AUDIO_RECORD_NUM)
			break;
	}

	/* Disable Aec */
	ret = IMP_AI_DisableAec(devID, chnID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio enable aec failed\n");
		return NULL;
	}

	/* Step 9: disable the audio channel. */
	ret = IMP_AI_DisableChn(devID, chnID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio channel disable error\n");
		return NULL;
	}

	/* Step 10: disable the audio devices. */
	ret = IMP_AI_Disable(devID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio device disable error\n");
		return NULL;
	}

	fclose(record_file);
	pthread_exit(0);
}

/* Test API */
static void* audio_encode_thread(void *argv)
{
	char *buf_pcm = NULL;
	int ret = -1;

	buf_pcm = (char *)malloc(IMP_AUDIO_BUF_SIZE);
	if(buf_pcm == NULL) {
		IMP_LOG_ERR(TAG, "malloc audio pcm buf error\n");
		return NULL;
	}

	FILE *file_pcm = fopen(argv, "rb");
	if(NULL == file_pcm) {
		IMP_LOG_ERR(TAG, "fopen %s failed\n",argv);
		return NULL;
	}

	FILE *file_encode = fopen(outFileName,"wb");
	if(NULL == file_encode) {
		 IMP_LOG_ERR(TAG, "fopen %s failed\n",outFileName);
		 return NULL;
	}

	/* audio encode create channel. */
	int AeChn = 0;
	IMPAudioEncChnAttr attr;

	if(0 == type)
		attr.type = PT_ADPCM;
	else if(1 == type)
		attr.type = PT_G711A;
	else if(2 == type)
		attr.type = PT_G711U;
	else if(3 == type)
		attr.type = PT_G726;
	else{
		printf("fun:%s,the encode type is not invalide.\n",__func__);
		return NULL;
	}

	attr.bufSize = 20;
	ret = IMP_AENC_CreateChn(AeChn, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "imp audio encode create channel failed\n");
		return NULL;
	}

	while(1) {
		ret = fread(buf_pcm, 1, IMP_AUDIO_BUF_SIZE, file_pcm);
		if(ret <= 0)
			break;

		/* Send a frame to encode. */
		IMPAudioFrame frm;
		frm.virAddr = (uint32_t *)buf_pcm;
		frm.len = ret;
		ret = IMP_AENC_SendFrame(AeChn, &frm);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode send frame failed\n");
			return NULL;
		}

		/* get audio encode frame. */
		IMPAudioStream stream;
		ret = IMP_AENC_PollingStream(AeChn, 1000);
		if (ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode polling stream failed\n");
			return NULL;
		}

		ret = IMP_AENC_GetStream(AeChn, &stream, BLOCK);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode get stream failed\n");
			return NULL;
		}

		/* save the encode data to the file. */
		fwrite(stream.stream, 1, stream.len, file_encode);

		/* release stream. */
		ret = IMP_AENC_ReleaseStream(AeChn, &stream);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode release stream failed\n");
			return NULL;
		}
	}

	 /*destroy the encode channel. */
	ret = IMP_AENC_DestroyChn(AeChn);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "imp audio encode destroy channel failed\n");
		return NULL;
	}

	fclose(file_pcm);
	fclose(file_encode);

	free(buf_pcm);
	return 0;
}

static int set_aencTest_param(TEST_ARGS *pTestArgs)
{
	if(NULL == pTestArgs) {
		printf("fun:%s,pTestArgs is empty\n",__func__);
		return -1;
	}

	if(strcmp(pTestArgs->inFileName,"") != 0)
		strncpy(inFileName,pTestArgs->inFileName,MAX_FILE_NAME_LEN);

	if(!strcmp(pTestArgs->outFileName,"")) {
		printf("aenc Test must set out file name.\n");
		return -1;
	}
	strncpy(outFileName,pTestArgs->outFileName,MAX_FILE_NAME_LEN);

	if(-1 == pTestArgs->type){
		printf("aenc Test must set enc type.\n");
		return -1;
	}
	type = pTestArgs->type;

	return 0;
}

int main(int argc,char *argv[])
{
	int ret = -1;

	ret = parse_param(argc,argv);
	if(1 == ret) {
		return 0;
	}

	if(ret < 0) {
		printf("parse_param failed\n");
		return -1;
	} else {
		printf("parse_param Ok.\n");
	}

	ret = set_aencTest_param(&testArgs);
	if(ret != 0){
		return -1;
	}

	printf("inFileName = %s\n",inFileName);
	printf("outFileName = %s\n",outFileName);
	printf("type = %d\n",type);

	pthread_t tid_record,tid_encode;

	if(!strcmp(inFileName,"ai")) {
		printf("Start to record pcm file for encode.\n");
		printf("Enter any key to continue.\n");
		getchar();

		ret = pthread_create(&tid_record,NULL,audio_record_thread,NULL);
		if(ret != 0) {
			printf("Create IMP_Audio_Record_Thread failed\n");
			return -1;
		}
		pthread_join(tid_record,NULL);

		ret = pthread_create(&tid_encode,NULL,audio_encode_thread,TMP_RECORD_FILE);
		if(ret != 0) {
			printf("Create IMP_Audio_Encode_Thread failed.\n");
			return -1;
		}
		pthread_join(tid_encode,NULL);

	} else if(strcmp(inFileName,"") != 0) {
		printf("Start to encode file %s\n",inFileName);
		printf("Enter any key to continue.\n");
		getchar();

		ret = pthread_create(&tid_encode,NULL,audio_encode_thread,inFileName);
		if(ret != 0){
			printf("Create IMP_Audio_Encode_Thread failed.\n");
			return -1;
		}
		pthread_join(tid_encode,NULL);
	}
	printf("Finished aenc test.\n");

	return 0;
}
